{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 第六节 LangChain 表达式语言 (LCEL)\n",
    "\n",
    "**从链到自定义链**\n",
    "\n",
    "**LangChain 表达式语言**，或 LCEL，是一种声明式的方法，可以轻松地将链组合在一起。 LCEL 从第一天开始设计就是为了 支持将原型直接投入生产，无需代码更改，从最简单的 “提示 + LLM” 链到最复杂的链（我们见过人们成功地在生产中运行具有数百个步骤的 LCEL 链）。以下是您可能想要使用 LCEL 的一些原因：\n",
    "\n",
    "**流式传输支持** 当您使用 LCEL 构建链时，您将获得最佳的首次令牌时间（直到第一个输出块出现的时间）。对于某些链，这意味着例如我们直接从 LLM 流式传输令牌到流式输出解析器，您将以与 LLM 提供者输出原始令牌相同的速率获得解析后的增量输出块。\n",
    "\n",
    "**异步支持** 使用 LCEL 构建的任何链都可以使用同步 API（例如在 Jupyter 笔记本中进行原型设计）以及异步 API（例如在 LangServe 服务器中）进行调用。这使得您可以在原型和生产中使用相同的代码，具有出色的性能，并且能够在同一个服务器上处理许多并发请求。\n",
    "\n",
    "**优化的并行执行** 每当您的 LCEL 链具有可以并行执行的步骤（例如，如果您从多个检索器中获取文档），我们会自动执行它，无论是在同步还是异步接口中，以实现尽可能小的延迟。\n",
    "\n",
    "**重试和回退** 为您的 LCEL 链的任何部分配置重试和回退。这是使您的链在规模上更可靠的绝佳方式。我们目前正在努力为重试/回退添加流式传输支持，以便您可以在没有任何延迟成本的情况下获得增加的可靠性。\n",
    "\n",
    "**访问中间结果** 对于更复杂的链，通常非常有用，即使在最终输出生成之前，也能访问中间步骤的结果。这可以用于让用户知道正在发生什么，或者仅仅是为了调试您的链。您可以流式传输中间结果，它在每个 LangServe 服务器上都是可用的。\n",
    "\n",
    "**输入和输出模式** 输入和输出模式为每个 LCEL 链提供了 Pydantic 和 JSONSchema 模式，这些模式是从您的链结构中推断出来的。这可以用于输入和输出的验证，并且是 LangServe 的一个重要组成部分。\n",
    "\n",
    "**无缝 LangSmith 跟踪集成** 随着您的链变得越来越复杂，越来越重要的是要了解每个步骤中确切发生的事情。 使用 LCEL，所有 步骤都自动记录到 LangSmith，以实现最大的可观察性和可调试性。\n",
    "\n",
    "**无缝 LangServe 部署集成** 使用 LCEL 创建的任何链都可以轻松地使用 LangServe 进行部署。\n",
    "\n",
    "https://langchain114.com/docs/expression_language\n",
    "\n",
    "https://blog.csdn.net/Attitude93/article/details/136531425"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "#导入语言模型\n",
    "import os\n",
    "from langchain_community.llms import Tongyi\n",
    "from langchain_community.llms import SparkLLM\n",
    "from langchain_community.llms import QianfanLLMEndpoint\n",
    "\n",
    "import pandas as pd\n",
    "#导入模版\n",
    "from langchain.prompts import PromptTemplate\n",
    "\n",
    "#导入聊天模型\n",
    "from langchain.prompts.chat import (\n",
    "    ChatPromptTemplate,\n",
    "    SystemMessagePromptTemplate,\n",
    "    AIMessagePromptTemplate,\n",
    "    HumanMessagePromptTemplate,\n",
    ")\n",
    "from langchain.schema import (\n",
    "    AIMessage,\n",
    "    HumanMessage,\n",
    "    SystemMessage\n",
    ")\n",
    "\n",
    "from langchain_community.chat_models import ChatSparkLLM\n",
    "from langchain_community.chat_models.tongyi import ChatTongyi\n",
    "from langchain_community.chat_models import QianfanChatEndpoint\n",
    "\n",
    "from langchain.chains import LLMChain\n",
    "\n",
    "#输入三个模型各自的key\n",
    "\n",
    "os.environ[\"DASHSCOPE_API_KEY\"] = \"\"\n",
    "\n",
    "os.environ[\"IFLYTEK_SPARK_APP_ID\"] = \"\"\n",
    "os.environ[\"IFLYTEK_SPARK_API_KEY\"] = \"\"\n",
    "os.environ[\"IFLYTEK_SPARK_API_SECRET\"] = \"\"\n",
    "\n",
    "os.environ[\"QIANFAN_AK\"] = \"\"\n",
    "os.environ[\"QIANFAN_SK\"] = \"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "model_ty = Tongyi(temperature=0.1)\n",
    "model_qf = QianfanLLMEndpoint(temperature=0.1)\n",
    "chat_ty = ChatTongyi()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 一个最简单的例子：prompt + model + output parser\n",
    "\n",
    "https://langchain114.com/docs/expression_language/interface"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.prompts import ChatPromptTemplate\n",
    "from langchain_core.output_parsers import StrOutputParser #仅仅是让输出对象成为字符串\n",
    "\n",
    "prompt = ChatPromptTemplate.from_template(\"告诉我一个有关 {topic}的笑话\")\n",
    "model = ChatTongyi()\n",
    "output_parser = StrOutputParser()\n",
    "\n",
    "chain = prompt | model | output_parser"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'当然，这是一个简短的绵羊笑话：\\n\\n为什么绵羊总是参加瑜伽课？ \\n\\n因为它们想变得更加咩力无穷（flexible）！'"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"topic\":\"绵羊\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content=\"Sure, here's a short ice cream joke for you:\\nWhy did the ice cream truck break down? Because it had a meltdown!\")"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"topic\":\"ice cream\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'title': 'PromptInput',\n",
       " 'type': 'object',\n",
       " 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}}"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.input_schema.schema()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'title': 'PromptInput',\n",
       " 'type': 'object',\n",
       " 'properties': {'topic': {'title': 'Topic', 'type': 'string'}}}"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prompt.input_schema.schema()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'title': 'StrOutputParserOutput', 'type': 'string'}"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.output_schema.schema()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 操作单变量输入和输出\n",
    "\n",
    "RunnableParallel 可用于操作一个 Runnable 的输出，使其匹配序列中下一个 Runnable 的输入格式。\n",
    "\n",
    "在这里，prompt 的输入预期是一个带有“context”和“question”键的映射。用户输入仅是问题。因此，我们需要使用检索器获取上下文，并将用户输入作为“question”键传递。\n",
    "\n",
    "* 代码中每个 \"|\" 前后的元素都可看作是一个Runnable。\n",
    "\n",
    "其中的 {\"context\": retriever, \"question\": RunnablePassthrough()} 就是RunnableParallel，它的作用就是将输入组成 context 和 question 为key的字典格式，传递给 prompt。\n",
    "\n",
    "RunnableParallel 的使用可以有以下三种形式，三种形式等价：\n",
    "\n",
    "* RunnableParallel({\"context\": retriever, \"question\": RunnablePassthrough()})\n",
    "* {\"context\": retriever,\"question\": RunnablePassthrough()}\n",
    "* RunnableParallel(context=retriever, question=RunnablePassthrough())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[INFO] [03-26 09:34:43] openapi_requestor.py:316 [t:5080]: requesting llm api endpoint: /embeddings/embedding-v1\n",
      "[INFO] [03-26 09:34:43] oauth.py:207 [t:5080]: trying to refresh access_token for ak `og6mWr***`\n",
      "[INFO] [03-26 09:34:43] oauth.py:220 [t:5080]: sucessfully refresh access_token\n"
     ]
    }
   ],
   "source": [
    "from langchain.prompts import ChatPromptTemplate\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.runnables import RunnablePassthrough\n",
    "from langchain.vectorstores import Chroma\n",
    "from langchain_community.embeddings import QianfanEmbeddingsEndpoint\n",
    "\n",
    "vectorstore = Chroma.from_texts(\n",
    "[\"小明在华为工作\"], embedding=QianfanEmbeddingsEndpoint()\n",
    ")\n",
    "retriever = vectorstore.as_retriever()\n",
    "template =\"\"\"仅根据以下上下文回答问题：\n",
    "{context}\n",
    "\n",
    "问题：{question}\n",
    "\"\"\"\n",
    "prompt = ChatPromptTemplate.from_template(template)\n",
    "model = ChatTongyi()\n",
    "\n",
    "retrieval_chain =(\n",
    "{\"context\": retriever,\"question\": RunnablePassthrough()}\n",
    "| prompt\n",
    "| model\n",
    "| StrOutputParser()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[INFO] [03-26 09:34:45] openapi_requestor.py:316 [t:6444]: requesting llm api endpoint: /embeddings/embedding-v1\n",
      "Number of requested results 4 is greater than number of elements in index 1, updating n_results = 1\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'根据上下文，无法确定小强在哪里工作。'"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "retrieval_chain.invoke(\"小强在哪里工作？\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### classwork 1\n",
    "\n",
    "* 把下面的代码改写成 LCEL 链的形式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'product': '彩色袜子',\n",
       " 'text': '当然可以，以下是五个为您的彩色袜子公司建议的中文名字：\\n\\n1. 彩韵袜业：这个名字融合了色彩和韵律的元素，暗示袜子的多彩和时尚感。\\n2. 明彩足下：寓意穿上公司的袜子，能让人的脚步充满明亮和活力，同时也突出了彩色的特点。\\n3. 七彩虹袜：七彩虹代表了所有颜色，简洁且易于记忆，让人联想到各种色彩的袜子。\\n4. 色舞足尖：形象地描绘出袜子的色彩在脚尖舞动的画面，富有动态美和艺术感。\\n5. 彩织悦步：强调袜子的制作精细（彩织）以及带给人们愉快的步伐体验。\\n\\n希望这些建议能对您有所帮助！如果您需要更多的选择或者有特定的要求，请告诉我。'}"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "llm = Tongyi(temperature=0.3)\n",
    " \n",
    "prompt = PromptTemplate(\n",
    "    input_variables=[\"product\"],\n",
    "    template=\"我希望你能充当新公司的命名顾问。一个生产{product}的公司好的中文名字是什么,请给出五个选项？\",\n",
    ")\n",
    "\n",
    "chain = LLMChain(llm=model_ty, prompt=prompt)\n",
    "\n",
    "chain.invoke(\"彩色袜子\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 把下面的代码改写成 LCEL 链的形式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.output_parsers import CommaSeparatedListOutputParser\n",
    "\n",
    "output_parser = CommaSeparatedListOutputParser()\n",
    "format_instructions = output_parser.get_format_instructions()\n",
    "prompt = PromptTemplate(\n",
    "    template=\"请列出五个 {subject}.\\n{format_instructions}\",\n",
    "    input_variables=[\"subject\"],\n",
    "    partial_variables={\"format_instructions\": format_instructions}\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "metadata": {},
   "outputs": [],
   "source": [
    "input0 = prompt.format(subject=\"中国的名山\")\n",
    "output = model_ty.invoke(input0)\n",
    "res0=output_parser.parse(output)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['泰山', '黄山', '华山', '衡山', '嵩山']"
      ]
     },
     "execution_count": 87,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "res0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 把下面的代码改写成 LCEL 链的形式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 124,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[INFO] [03-25 23:25:55] openapi_requestor.py:316 [t:32248]: requesting llm api endpoint: /embeddings/embedding-v1\n"
     ]
    }
   ],
   "source": [
    "from langchain.chains.combine_documents import create_stuff_documents_chain\n",
    "from langchain.chains import create_retrieval_chain\n",
    "from langchain.document_loaders import TextLoader\n",
    "from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
    "\n",
    "model_qf = QianfanLLMEndpoint(temperature=0.1)\n",
    "loader_txt = TextLoader(r'G:\\云岚宗.txt', encoding='utf8')\n",
    "docs_txt = loader_txt.load()\n",
    "text_splitter_txt = RecursiveCharacterTextSplitter(chunk_size = 384, chunk_overlap = 0, separators=[\"\\n\\n\", \"\\n\", \" \", \"\", \"。\", \"，\"])\n",
    "documents_txt = text_splitter_txt.split_documents(docs_txt)\n",
    "embeddings_qf = QianfanEmbeddingsEndpoint()\n",
    "vectordb = Chroma.from_documents(documents=documents_txt, embedding=embeddings_qf)\n",
    "\n",
    "prompt = ChatPromptTemplate.from_template(\"\"\"使用下面的语料来回答本模板最末尾的问题。如果你不知道问题的答案，直接回答 \"我不知道\"，禁止随意编造答案。\n",
    "        为了保证答案尽可能简洁，你的回答必须不超过三句话，你的回答中不可以带有星号。\n",
    "        请注意！在每次回答结束之后，你都必须接上 \"感谢你的提问\" 作为结束语\n",
    "        以下是一对问题和答案的样例：\n",
    "            请问：秦始皇的原名是什么\n",
    "            秦始皇原名嬴政。感谢你的提问。\n",
    "        \n",
    "        以下是语料：\n",
    "<context>\n",
    "{context}\n",
    "</context>\n",
    "\n",
    "Question: {input}\"\"\")\n",
    "#创建检索链\n",
    "document_chain = create_stuff_documents_chain(model_qf, prompt)\n",
    "\n",
    "retriever = vectordb.as_retriever()\n",
    "retrieval_chain = create_retrieval_chain(retriever, document_chain)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[INFO] [03-25 23:19:10] openapi_requestor.py:316 [t:5608]: requesting llm api endpoint: /embeddings/embedding-v1\n",
      "[INFO] [03-25 23:19:10] openapi_requestor.py:316 [t:31424]: requesting llm api endpoint: /chat/eb-instant\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "纳兰桀是加玛帝国狮心元帅纳兰桀的孙儿。\n"
     ]
    }
   ],
   "source": [
    "response = retrieval_chain.invoke({\n",
    "    \"input\": \"纳兰桀是谁?\"\n",
    "})\n",
    "print(response[\"answer\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 多变量输入输出的操作\n",
    "\n",
    "* 使用 Python 的 itemgetter 作为速记，从映射中提取数据时与 RunnableParallel 结合使用\n",
    "\n",
    "* 代码中每个 \"|\" 前后的元素都可看作是一个Runnable。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 (1, 2, 3)\n"
     ]
    }
   ],
   "source": [
    "from operator import itemgetter\n",
    "a = [1,2,3,4,5]\n",
    "b = itemgetter(0)\n",
    "c = itemgetter(0,1,2)\n",
    "print(b(a),c(a))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "90\n"
     ]
    }
   ],
   "source": [
    "dict0={\"语文\":80,\"数学\":90,\"英语\":70,\"物理\":92,\"化学\":83}\n",
    "b=itemgetter(\"数学\")\n",
    "print(b(dict0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 120,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[INFO] [03-25 23:23:16] openapi_requestor.py:316 [t:32248]: requesting llm api endpoint: /embeddings/embedding-v1\n"
     ]
    }
   ],
   "source": [
    "from langchain.prompts import ChatPromptTemplate\n",
    "from langchain_community.vectorstores import Chroma\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.runnables import RunnablePassthrough\n",
    "from langchain_community.embeddings import QianfanEmbeddingsEndpoint\n",
    "\n",
    "vectorstore = Chroma.from_texts(\n",
    "[\"小明在华为工作\"], embedding=QianfanEmbeddingsEndpoint()\n",
    ")\n",
    "retriever = vectorstore.as_retriever()\n",
    "\n",
    "template =\"\"\"仅根据以下上下文回答问题：\n",
    "{context}\n",
    "\n",
    "问题：{question}\n",
    "\n",
    "请使用以下语言回答：{language}\n",
    "\"\"\"\n",
    "prompt = ChatPromptTemplate.from_template(template)\n",
    "\n",
    "chain =(\n",
    "{\n",
    "\"context\": itemgetter(\"question\")| retriever,\n",
    "\"question\": itemgetter(\"question\"),\n",
    "\"language\": itemgetter(\"language\"),\n",
    "}\n",
    "| prompt\n",
    "| model_ty\n",
    "| StrOutputParser()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 121,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[INFO] [03-25 23:23:18] openapi_requestor.py:316 [t:10960]: requesting llm api endpoint: /embeddings/embedding-v1\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'Xiaoming works at Huawei.'"
      ]
     },
     "execution_count": 121,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"question\":\"小明在哪里工作\",\"language\":\"英语\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 传递数据\n",
    "\n",
    "RunnablePassthrough 允许将输入不变地传递或添加额外的键。这通常与 RunnableParallel 结合使用，将数据分配给映射中的新键。\n",
    "\n",
    "单独调用 RunnablePassthrough() 时，它将简单地接收输入并传递。\n",
    "\n",
    "使用分配（RunnablePassthrough.assign(...)）调用 RunnablePassthrough 时，它将接收输入，并将添加给分配函数的额外参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'passed': {'num': 1}, 'extra': {'num': 1, 'mult': 3}, 'modified': 2}"
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.runnables import RunnableParallel, RunnablePassthrough\n",
    "\n",
    "runnable = RunnableParallel(\n",
    "    passed=RunnablePassthrough(),\n",
    "    extra=RunnablePassthrough.assign(mult=lambda x: x[\"num\"]*3),\n",
    "    modified=lambda x: x[\"num\"]+1,\n",
    ")\n",
    "\n",
    "runnable.invoke({\"num\":1})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### classwork 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = PromptTemplate(\n",
    "    input_variables=[\"product\",\"style\",\"num\"],\n",
    "    template=\"我希望你能充当新公司的命名顾问。公司新推出一款{product}产品，请帮给新产品取一个{style}中文名字，给出{num}个选项？\",\n",
    ")\n",
    "\n",
    "chain_m = LLMChain(llm=model_ty, prompt=prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'product': '彩色袜子',\n",
       " 'style': '可爱',\n",
       " 'num': 3,\n",
       " 'text': '当然可以，以下是我为您的彩色袜子产品提出的三个命名建议：\\n\\n1. \"彩虹脚步\"：这个名字寓含了袜子的多彩特性，同时也传达出活力和快乐的情绪，仿佛每一步都踩在彩虹上。\\n2. \"萌彩袜趣\"： \"萌\"字代表可爱，\"彩\"代表颜色丰富，\"袜趣\"则强调产品的趣味性和个性化，适合年轻人和孩子们。\\n3. \"七彩踏云\"： \"七彩\"代表多种颜色，\"踏云\"则给人一种轻盈、舒适的想象，适合用来形容穿着舒适，色彩丰富的袜子。\\n\\n希望这些建议能对您有所帮助！'}"
      ]
     },
     "execution_count": 66,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain_m.invoke({\"product\":\"彩色袜子\",\"style\":\"可爱\",\"num\":3})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 改写上面的代码，用lcel实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 改写上面的代码，用lcel实现，但数量要在输入的基础上加一"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 改写上面《云岚宗》的rag案例，通过itemgetter实现参数的传入"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 链与链的组合"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "#https://blog.csdn.net/oHeHui1/article/details/136038434\n",
    "\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "\n",
    "llm = ChatTongyi()\n",
    "\n",
    "prompt = ChatPromptTemplate.from_template(\"only give me the result,no other words:the result of add 20 to {expression1}\")\n",
    "\n",
    "chain_one = prompt | llm | StrOutputParser()\n",
    "\n",
    "second_prompt = ChatPromptTemplate.from_template(\n",
    "    \"only give me the sum result,no other words: the result of add {expression1} to {expression2}\"\n",
    ")\n",
    "\n",
    "chain_two = (\n",
    "    {\"expression1\": chain_one, \"expression2\": itemgetter(\"expression2\")}\n",
    "    | second_prompt\n",
    "    | llm\n",
    "    | StrOutputParser()\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The sum is 23.\n"
     ]
    }
   ],
   "source": [
    "print(chain_two.invoke({\"expression1\": \"3\",\"expression2\": \"10\"}))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 自定义函数\n",
    "\n",
    "* RunnableLambda"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.runnables import RunnableLambda\n",
    "\n",
    "\n",
    "def length_function(text):\n",
    "    return len(text)\n",
    "\n",
    "\n",
    "def _multiple_length_function(text1, text2):\n",
    "    return len(text1) * len(text2)\n",
    "\n",
    "\n",
    "def multiple_length_function(_dict):\n",
    "    return _multiple_length_function(_dict[\"text1\"], _dict[\"text2\"])\n",
    "\n",
    "\n",
    "prompt = ChatPromptTemplate.from_template(\"what is {a} + {b}\")\n",
    "\n",
    "chain1 = prompt | model_ty\n",
    "\n",
    "chain =(\n",
    "{\n",
    "    \"a\": itemgetter(\"foo\") | RunnableLambda(length_function),\n",
    "    \"b\": {\"text1\": itemgetter(\"foo\"), \"text2\": itemgetter(\"bar\")}\n",
    "    | RunnableLambda(multiple_length_function),\n",
    "}\n",
    "| prompt\n",
    "| model_ty\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'3 + 9 is equal to 12.'"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"foo\":\"bar\",\"bar\":\"gah\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 参考链接\n",
    "\n",
    "https://blog.csdn.net/weixin_50516745/article/details/132103832\n",
    "\n",
    "https://blog.csdn.net/qq128252/article/details/132870134\n",
    "\n",
    "https://gitcode.csdn.net/65ed78c81a836825ed79a6d9.html\n",
    "\n",
    "https://www.jianshu.com/p/9e646767e5b6\n",
    "\n",
    "https://brightinventions.pl/blog/introducing-langchain-agents-tutorial-with-example/\n",
    "\n",
    "https://api.python.langchain.com/en/latest/langchain_api_reference.html#module-langchain.agents\n",
    "\n",
    "https://wangwei1237.github.io/LLM_in_Action/langchain_agent_react.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* LECL\n",
    "\n",
    "https://langchain114.com/docs/expression_language/why\n",
    "\n",
    "https://blog.csdn.net/Attitude93/article/details/136531425\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* RAG\n",
    "\n",
    "https://cloud.tencent.com.cn/developer/article/2369731"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
